package com.caimiao.miao.service;

import android.bluetooth.BluetoothGatt;
import android.content.Context;

import com.caimiao.miao.http.manager.TicketOutHttp;
import com.caimiao.miao.http.rxjava.transformer.SchedulerTransformer;
import com.caimiao.miao.interfaces.BasePresenterImpl;
import com.caimiao.miao.library.bluetooth.state.BtTicketState;
import com.caimiao.miao.library.bluetooth.state.IBtState;
import com.caimiao.miao.library.utils.CRC16Util;
import com.caimiao.miao.library.utils.tip.PrintLog;

import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;

public class BlePresenter extends BasePresenterImpl<IBleContact.View, IBleContact.Model> implements IBleContact.Presenter {
    private ObservableEmitter mEmitter;
    private Disposable mRecTimer;

    public BlePresenter(IBleContact.View mView) {
        super(mView);
    }

    @Override
    protected IBleContact.Model getModel() {
        return new BleModel();
    }

    @Override
    public void start(Context context) {
        mModel.initBluetooth(context);
        Disposable disposable = mModel.getBtState()
                .observeOn(Schedulers.newThread())
                .subscribe(new Consumer<IBtState>() {
                    @Override
                    public void accept(IBtState iBtState) throws Exception {
                        switch (iBtState.getCode()) {
                            case IBtState.CODE_WRITE:
                                if (iBtState.getState() != BluetoothGatt.GATT_SUCCESS) {
                                    sendTicket(false);
                                }
                                break;
                            case IBtState.CODE_SERVICE:
                                if (iBtState.getState() == BluetoothGatt.GATT_SUCCESS) {
                                    if (mModel.needOut()) {//如果正在出票，说明这个是蓝牙重连的反馈
                                        //等待10秒，如果未被取消，则重发当前命令
                                        mRecTimer = Observable.timer(3, TimeUnit.SECONDS)
                                                .subscribe(new Consumer<Long>() {
                                                    @Override
                                                    public void accept(Long aLong) throws Exception {
                                                        sendTicket(true);
                                                    }
                                                });
                                    }
                                }
                                break;
                            case IBtState.CODE_TICKET:
                                break;
                        }
                    }
                });
        addDisposable(disposable);
    }

    @Override
    public void detach() {
        super.detach();
        mModel.closeBle();
    }

    @Override
    public Observable<IBtState> setOutTicket(int num) {
        mModel.setTicketOutNum(num);
        setReadMonitor();
        sendNextTicket();
        return getTicketSubscribe();
    }

    @Override
    public void setReOutTicket() {
        sendTicket(true);
    }

    @Override
    public void postTickOut(Context context) {
        Disposable disposable = new TicketOutHttp().outTicket();
        addDisposable(disposable);
    }

    //判断并发送出票指令
    private void sendNextTicket() {
        mModel.clearReadBufferData();
        if (mModel.needOut()) {
            sendTicket(false);
        } else {
            ///出票完成
            if (mEmitter != null) mEmitter.onNext(new BtTicketState(0, null));
        }
    }

    //发送出票指令
    private void sendTicket(boolean isResend) {
        byte[] bytes = mModel.getCommand(isResend);
        PrintLog.printBle("SET: " + CRC16Util.getBufHexStr(bytes));
        mModel.write(bytes);
    }

    private void setReadMonitor() {
        Disposable disposable = mModel.getBtRead()
                .map(new Function<byte[], Integer>() {
                    @Override
                    public Integer apply(byte[] bytes) throws Exception {
                        int ret = -1;
                        if (mRecTimer != null) mRecTimer.dispose();

                        mModel.setReadBufferData(bytes);
                        byte[] readData = mModel.getReadBufferData();
                        PrintLog.printBle("GET: " + CRC16Util.getBufHexStr(readData));
                        ///出票返回指令(长度位8)：0xff 0x55 0xa2 0x02 0x(剩余) 0x(结果) CRC-H CRC-L
                        int length = 8;
                        for (int i = 0; i < readData.length - 2; i++) {
                            if (readData[i + 0] == (byte) 0xff && readData[i + 1] == (byte) 0x55
                                    && readData[i + 2] == (byte) 0xa2 && (readData.length - i) >= length) {
                                byte[] command = new byte[length];
                                System.arraycopy(readData, i, command, 0, length);
                                //校验CRC
                                if (CRC16Util.checkBuf(command)) {
                                    //清除缓存
                                    mModel.removeReadBufferByIndex(i + length);
                                    if (command[5] == 0) {
                                        mModel.setTicketOutRemain(command[4]);//减去一票
                                        sendNextTicket();//下一张票(是否需要再次传送)
                                    }
                                    ret = command[5];
                                }
                            }
                        }
                        return ret;
                    }
                })
                .subscribeOn(Schedulers.newThread())
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer integer) throws Exception {
                        if (integer >= 0)
                            mEmitter.onNext(new BtTicketState(integer, String.valueOf(mModel.getTicketOutRemain())));
                    }
                });
        addDisposable(disposable);
    }

    private Observable<IBtState> getTicketSubscribe() {
        return Observable.create(new ObservableOnSubscribe<IBtState>() {
            @Override
            public void subscribe(ObservableEmitter<IBtState> emitter) throws Exception {
                mEmitter = emitter;
            }
        });
    }
}
